package io.lvdaxian.mybatis19.reflection;

import io.lvdaxian.mybatis19.reflection.invoker.GetFieldInvoker;
import io.lvdaxian.mybatis19.reflection.invoker.Invoker;
import io.lvdaxian.mybatis19.reflection.invoker.MethodInvoker;
import io.lvdaxian.mybatis19.reflection.invoker.SetFieldInvoker;
import io.lvdaxian.mybatis19.reflection.property.PropertyNamer;
import javassist.Modifier;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ReflectPermission;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 基础反射器 属性 get 以及set方法的映射器
 *
 * @author lihh
 */
public class Reflector {
  private static boolean classCacheEnabled = false;
  private final static String[] EMPTY_STRING_ARRAY = new String[0];
  
  // 线程安全的缓存
  private final static ConcurrentMap<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<>();
  // 这个表示类本身
  private Class<?> type;
  
  // get 属性列表
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
  // set 属性列表
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
  // set 方法列表
  private final Map<String, Invoker> setMethods = new HashMap<>();
  // get 方法列表
  private final Map<String, Invoker> getMethods = new HashMap<>();
  // set 类型列表
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  // get 类型列表
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  
  // 表示 构造方法
  private Constructor<?> defaultConstructor;
  private final Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
  
  public Reflector(Class<?> clazz) {
    // 表示 类本身
    this.type = clazz;
    
    // 设置 默认的构造方法
    addDefaultConstructor(clazz);
    // 刨析 getter 方法
    addGetMethods(clazz);
    // 刨析 setter 方法
    addSetMethods(clazz);
    
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    // 保证属性名称 是大写转换
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }
  
  /**
   * 剥离 类字段的方法
   *
   * @param clazz 类
   * @author lihh
   */
  private void addFields(Class<?> clazz) {
    // 这里 得到所有的属性
    Field[] fields = clazz.getDeclaredFields();
    
    for (Field field : fields) {
      if (canAccessPrivateMethods()) {
        // 设置 可以访问
        field.setAccessible(true);
      }
      
      if (field.isAccessible()) {
        // 表示 集合【setMethods】中 key 是不存在的
        if (!setMethods.containsKey(field.getName())) {
          // issue #379 - removed the check for final because JDK 1.5 allows
          // modification of final fields through reflection (JSR-133). (JGB)
          // pr #16 - final static can only be set by the classloader
          int modifiers = field.getModifiers();
          // 找到 非私有 && 非静态的
          if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
            addSetField(field);
          }
        }
        
        if (!getMethods.containsKey(field.getName()))
          addGetField(field);
      }
    }
    
    // 以 迭代的形式 添加父类
    if (null != clazz.getSuperclass())
      addFields(clazz.getSuperclass());
  }
  
  /**
   * 添加 field 字段
   *
   * @param field 字段
   * @author lihh
   */
  private void addSetField(Field field) {
    if (!isValidPropertyName(field.getName()))
      return;
    
    setMethods.put(field.getName(), new SetFieldInvoker(field));
    setTypes.put(field.getName(), field.getType());
  }
  
  /**
   * 这是 添加 get field
   *
   * @param field 字段
   * @author lihh
   */
  private void addGetField(Field field) {
    if (!isValidPropertyName(field.getName()))
      return;
    
    getMethods.put(field.getName(), new GetFieldInvoker(field));
    getTypes.put(field.getName(), field.getType());
  }
  
  /**
   * 判断 系统资源访问权限
   *
   * @return 返回的状态
   * @author lihh
   */
  private static boolean canAccessPrivateMethods() {
    SecurityManager securityManager = System.getSecurityManager();
    
    try {
      if (null != securityManager)
        securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
    } catch (SecurityException e) {
      return false;
    }
    
    return true;
  }
  
  /**
   * 尝试 添加默认的构造方法
   *
   * @param clazz 传递的类
   * @author lihh
   */
  private void addDefaultConstructor(Class<?> clazz) {
    // 这里 是获取所有的构造方法
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    
    // 遍历构造方法
    for (Constructor<?> constructor : constructors) {
      // 判断 是否是无参构造方法
      if (0 == constructor.getParameterCount()) {
        // 如果是存在安全限制访问的话，设置构造方法可以被方法
        if (canAccessPrivateMethods()) {
          constructor.setAccessible(true);
        }
        
        // 如果构造方法可以被访问的话，直接给 默认的构造函数
        if (constructor.isAccessible())
          this.defaultConstructor = constructor;
      }
    }
  }
  
  /**
   * 解析 get 方法，添加到集合中
   *
   * @param clazz 传递的类
   * @author lihh
   */
  private void addGetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    
    // 拿到类下的所有方法，其中包括父类的方法
    Method[] methods = getClassMethods(clazz);
    for (Method method : methods) {
      String name = method.getName();
      
      // 如果是 getXXX的话，最起码长度是4位，反之就是不合理
      if (name.startsWith("get") && name.length() > 3) {
        // 如果是 标准的get方法的话 一定是空参方法
        if (0 == method.getParameterCount()) {
          name = PropertyNamer.methodToProperty(name);
          // 将 name and method 添加到集合中
          addMethodConflict(conflictingGetters, name, method);
        }
        
        // 标准的 isXXX 的判断
      } else if (name.startsWith("is") && name.length() > 2) {
        if (0 == method.getParameterCount()) {
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      }
    }
    
    // 解析 getter
    resolveGetterConflicts(conflictingGetters);
  }
  
  /**
   * 添加方法 到  Conflict
   *
   * @param conflictingMethods 存放 name Method的集合
   * @param name               名称
   * @param method             方法
   * @author lihh
   */
  private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    List<Method> methods = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
    methods.add(method);
  }
  
  /**
   * 解析 getter 的方法
   *
   * @param conflictingGetters 冲突的getter。可能同一个名称下 方法有自己实现的 父类也有的
   * @author lihh
   */
  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (String propName : conflictingGetters.keySet()) {
      // 拿到所有的 getter 方法
      List<Method> getters = conflictingGetters.get(propName);
      
      // 这里 拿到 getters的迭代器
      Iterator<Method> iterator = getters.iterator();
      // 这里拿到第一个元素
      Method firstMethod = iterator.next();
      
      if (1 == getters.size())
        addGetMethod(propName, firstMethod);
      else {
        Method getter = firstMethod;
        // 从这里 拿到返回类型
        Class<?> getterType = firstMethod.getReturnType();
        
        // 通过迭代器 进行遍历
        while (iterator.hasNext()) {
          // 这个方法最起码从 2+ 以后开始的
          Method method = iterator.next();
          Class<?> methodType = method.getReturnType();
          
          if (methodType.equals(getterType)) {
            throw new RuntimeException("Illegal overloaded getter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass()
                + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
            
            // 判断类型【getterType】是否可以赋值给 methodType
          } else if (methodType.isAssignableFrom(getterType)) {
            // OK getter type is descendant
          } else if (getterType.isAssignableFrom(methodType)) {
            getter = method;
            getterType = methodType;
          } else {
            throw new RuntimeException("Illegal overloaded getter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass()
                + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
          }
        }
        addGetMethod(propName, getter);
      }
    }
  }
  
  /**
   * 根据类 添加 set 方法
   *
   * @param clazz 类本身
   * @author lihh
   */
  private void addSetMethods(Class<?> clazz) {
    // 这是 存放setter 的集合
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    // 执行到这里是拿到所有的方法
    Method[] methods = getClassMethods(clazz);
    
    for (Method method : methods) {
      
      String name = method.getName();
      if (!PropertyNamer.isSetter(name) || name.length() <= 3 || 1 != method.getParameterCount())
        continue;
      
      // 能执行到这里的话，必须满足格式：setXXX && 函数的参数只有一个
      name = PropertyNamer.methodToProperty(name);
      addMethodConflict(conflictingSetters, name, method);
    }
    
    resolveSetterConflicts(conflictingSetters);
  }
  
  /**
   * 解析 冲突的setter
   *
   * @param conflictingSetters 所有方法的集合
   * @author lihh
   */
  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (String propName : conflictingSetters.keySet()) {
      // 从这里拿到 setter 集合方法
      List<Method> setters = conflictingSetters.get(propName);
      
      Method firstMethod = setters.get(0);
      if (1 == setters.size())
        addSetMethod(propName, firstMethod);
      else {
        // 其实 get 以及set 方法都是一一先对的，所以通过propName中拿到 返回类型
        Class<?> expectedType = getTypes.get(propName);
        
        // 如果为null的话，一定是不满足需求的
        if (expectedType == null) {
          throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property "
              + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
              "specification and can cause unpredicatble results.");
        } else {
          Iterator<Method> methods = setters.iterator();
          Method setter = null;
          
          while (methods.hasNext()) {
            Method method = methods.next();
            if (method.getParameterTypes().length == 1
                && expectedType.equals(method.getParameterTypes()[0])) {
              setter = method;
              break;
            }
          }
          
          if (setter == null) {
            throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
                "specification and can cause unpredicatble results.");
          }
          addSetMethod(propName, setter);
        }
      }
    }
  }
  
  /**
   * 添加 设置的方法
   *
   * @param name   方法名称
   * @param method 方法本身
   * @author lihh
   */
  private void addSetMethod(String name, Method method) {
    // 判断 是否为有效的名字
    if (!isValidPropertyName(name))
      return;
    
    // 添加方法
    setMethods.put(name, new MethodInvoker(method));
    // 设置 参数类型
    setTypes.put(name, method.getParameterTypes()[0]);
  }
  
  /**
   * 添加 get 方法
   *
   * @param name   名称
   * @param method 方法
   * @author lihh
   */
  private void addGetMethod(String name, Method method) {
    if (!isValidPropertyName(name))
      return;
    
    getMethods.put(name, new MethodInvoker(method));
    // 这里是返回类型
    getTypes.put(name, method.getReturnType());
  }
  
  /**
   * 判断 是否为有效的名称
   *
   * @param name 方法名称
   * @return 是否有效的名称
   * @author lihh
   */
  private boolean isValidPropertyName(String name) {
    return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
  }
  
  /**
   * 通过反射 拿到类的方法
   *
   * @param cls 类字节
   * @return 返回 方法数组
   * @author lihh
   */
  private Method[] getClassMethods(Class<?> cls) {
    // 放置 方法的集合
    Map<String, Method> uniqueMethods = new HashMap<String, Method>();
    
    // 当前类
    Class<?> currentClass = cls;
    while (null != currentClass) {
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
      
      // 从这里返回一个接口数组
      Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> inter : interfaces)
        addUniqueMethods(uniqueMethods, inter.getDeclaredMethods());
      
      // 将当前 class 赋值给父类
      currentClass = currentClass.getSuperclass();
    }
    
    Collection<Method> methods = uniqueMethods.values();
    
    // 方法 方法数组
    return methods.toArray(new Method[methods.size()]);
  }
  
  /**
   * 这里 构建唯一的方法
   *
   * @param uniqueMethods 表示唯一方法的集合
   * @param methods       拿到的所有方法
   * @author lihh
   */
  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      // 说明 该方法是通过继承得来的
      if (currentMethod.isBridge())
        continue;
      
      // 拿到方法签名
      String signature = getSignature(currentMethod);
      // 判断是否唯一
      if (uniqueMethods.containsKey(signature))
        continue;
      
      // 这里进行权限判断 从而设置权限
      if (canAccessPrivateMethods()) {
        currentMethod.setAccessible(true);
      }
      
      uniqueMethods.put(signature, currentMethod);
    }
  }
  
  /**
   * 根据方法 拿到签名
   *
   * @param method 方法
   * @return 签名字符串
   * @author lihh
   */
  private String getSignature(Method method) {
    StringBuilder sb = new StringBuilder();
    
    // 将 返回值 添加到buffer中
    Class<?> returnType = method.getReturnType();
    if (null != returnType) {
      sb.append(returnType.getName()).append('#');
    }
    
    // 将方法名称 添加到 buffer中
    sb.append(method.getName());
    
    // 构建 方法的多个参数
    Class<?>[] parameters = method.getParameterTypes();
    for (int i = 0; i < parameters.length; i++) {
      if (i == 0) {
        sb.append(':');
      } else {
        sb.append(',');
      }
      sb.append(parameters[i].getName());
    }
    return sb.toString();
  }
  
  /**
   * 获取默认的构造方法
   *
   * @return 返回构造方法
   * @author lihh
   */
  public Constructor<?> getDefaultConstructor() {
    if (null != this.defaultConstructor)
      return this.defaultConstructor;
    throw new RuntimeException("There is no default constructor for " + type);
  }
  
  /**
   * 是否存在 默认的构造方法
   *
   * @return 返回存在状态
   * @author lihh
   */
  public boolean hasDefaultConstructor() {
    return null != this.defaultConstructor;
  }
  
  /**
   * 获取 setXX 参数类型
   *
   * @param propertyName 属性名称
   * @return 返回类型 Class
   * @author lihh
   */
  public Class<?> getSetterType(String propertyName) {
    // 这里 获取 set 参数类型
    Class<?> clazz = setTypes.get(propertyName);
    if (null == clazz)
      throw new RuntimeException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    
    return clazz;
  }
  
  /**
   * 这里 拿到 getter 方法的执行器
   *
   * @param propertyName 属性名称
   * @return 返回执行器
   * @author lihh
   */
  public Invoker getGetInvoker(String propertyName) {
    Invoker invoker = getMethods.get(propertyName);
    if (null == invoker)
      throw new RuntimeException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    
    return invoker;
  }
  
  /**
   * 拿到 setter 方法的执行器
   *
   * @param propertyName 属性名称
   * @return 返回 setter 执行器
   * @author lihh
   */
  public Invoker getSetInvoker(String propertyName) {
    Invoker invoker = setMethods.get(propertyName);
    if (null == invoker)
      throw new RuntimeException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    
    return invoker;
  }
  
  /**
   * 这里 拿到 getter 返回类型
   *
   * @param propertyName 属性名称
   * @return 返回类型
   * @author lihh
   */
  public Class<?> getGetterType(String propertyName) {
    Class<?> clazz = getTypes.get(propertyName);
    if (null == clazz)
      throw new RuntimeException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    
    return clazz;
  }
  
  /*
   * Gets an array of the readable properties for an object
   *
   * @return The array
   */
  public String[] getGetablePropertyNames() {
    return readablePropertyNames;
  }
  
  /*
   * Gets an array of the writeable properties for an object
   *
   * @return The array
   */
  public String[] getSetablePropertyNames() {
    return writeablePropertyNames;
  }
  
  /*
   * Check to see if a class has a writeable property by name
   *
   * @param propertyName - the name of the property to check
   * @return True if the object has a writeable property by the name
   */
  public boolean hasSetter(String propertyName) {
    return setMethods.keySet().contains(propertyName);
  }
  
  /*
   * Check to see if a class has a readable property by name
   *
   * @param propertyName - the name of the property to check
   * @return True if the object has a readable property by the name
   */
  public boolean hasGetter(String propertyName) {
    return getMethods.keySet().contains(propertyName);
  }
  
  public String findPropertyName(String name) {
    return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
  }
  
  /*
   * Gets an instance of ClassInfo for the specified class.
   * 得到某个类的反射器，是静态方法，而且要缓存，又要多线程，所以REFLECTOR_MAP是一个ConcurrentHashMap
   *
   * @param clazz The class for which to lookup the method cache.
   * @return The method cache for the class
   */
  public static Reflector forClass(Class<?> clazz) {
    if (classCacheEnabled) {
      // synchronized (clazz) removed see issue #461
      // 对于每个类来说，我们假设它是不会变的，这样可以考虑将这个类的信息(构造函数，getter,setter,字段)加入缓存，以提高速度
      Reflector cached = REFLECTOR_MAP.get(clazz);
      if (cached == null) {
        cached = new Reflector(clazz);
        REFLECTOR_MAP.put(clazz, cached);
      }
      return cached;
    } else {
      return new Reflector(clazz);
    }
  }
  
  public static void setClassCacheEnabled(boolean classCacheEnabled) {
    Reflector.classCacheEnabled = classCacheEnabled;
  }
  
  public static boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }
}
